
import urllib2

from django.db import models
from django.contrib.auth.models import Group
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _


#
# TODO: faltan hacer los custom validates para ``is_not_circular``
# TODO: y para ``validate_external_url``.
#

URL_VALIDATOR_USER_AGENT = "Mozilla/5.0 (X11; U; Linux i686; en-US; "\
              "rv:1.9.1.8) Gecko/20100214 Ubuntu/9.10 (karmic) Firefox/3.5.8"



# Menu options that take into account the type of user.

MENU_USER_TYPES = (
    ('A', _('Anonymous')),
    ('L', _('Logged In')),
    ('S', _('Staff')),
    ('X', _('Super User'))
)

def is_not_circular(field_data, all_data):
    """ Validates that there is no circular reference.
    This means that it validates for example that if A is parent of B and B
    is parent of C, then C can't be parent of A.

    :parameters:
        all_data: dict()
            all the data of the form.
    
    :raises:
        ValidationError: if there is a circular reference.
    """
    if not all_data.get('id', None) or not all_data.get('parent', None):
        return

    child_id = int(all_data['id'])
    parent_id = int(all_data['parent'])
    try:
        while paren_id:
            parent = NavBarEntry.objects.get(pk=parent_id)
            parent_id = parent.id
            if child_id == parent_id:
                raise ValidationError("There is cyclical reference.")
    except NavBarEntry.DoesNotExist:
        raise ValidationError("Couldn't find parent: %s" % parent_id)

def validate_external_url(field_data, all_data):
    """ Validates that if the url is external then it exists.
    An url will be external if it begins with http:// or with
    www.

    :parameters:
        field_data: str
            the url to check if exists.

    :raises:
        ValidationError: if the url is external and it doesn't exists.
    """    
    if field_data.startswith('http') or field_data.startswith('www.'):
        headers = {
            "Accept": "text/xml,application/xml,application/xhtml+xml,"
                      "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
            "Accept-Language": "en-us,en;q=0.5",
            "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
            "Connection": "close",
            "User-Agent": URL_VALIDATOR_USER_AGENT,
        }
        try:
            req = urllib2.Request(field_data, None, headers)
            u = urllib2.urlopen(req)
        except ValueError:
            raise ValidationError(u'Enter a valid URL.')
        except: # urllib2.URLError, httplib.InvalidURL, etc.
            raise ValidationError(u'This URL appears to be a broken link.')


class MenuEntry(models.Model):
    """ An entry in the menu.

    There are two security options:
        - user_type: indicates a greate scale the different types 
    of users.
        - groups: mainly used when the entry is for staff, to take
    into account the different roles of the staff.
    """
    name   = models.CharField(max_length=50,
                              help_text=_("text seen in the menu"))
    title  = models.CharField(max_length=50, blank=True,
                              help_text=_("mouse hover description"))
    url    = models.CharField(max_length=200)                                                
    order  = models.IntegerField(default=0)
    parent = models.ForeignKey('self', related_name='children',
                               blank=True, null=True)

    ## advanced permissions
    groups    = models.ManyToManyField(Group, null=True, blank=True)
    user_type = models.CharField(help_text=_('user login type'), max_length=1,
                                 choices=MENU_USER_TYPES, default='A')

    class Meta:
        verbose_name = 'navigation bar element'
        verbose_name_plural = 'navigation bar elements'
        #order_with_respect_to = 'parent' # doesn't woth with self relations
        #ordering = ('parent', 'order', 'name', 'url')

    class Admin:
        fields = (
            (None, {'fields': ('name', 'title', 'url', 'order', 'parent')}),
            ('Advanced Permissions', {'classes': 'collapse',
                             'fields': ('groups', )}),
        )
        list_filter = ('parent',)
        list_display = ('name', 'url', 'order', 'parent')
        search_fields = ('url', 'name', 'title')

    def __unicode__(self):
        return self.name
